package edu.wpi.cs509.representation;

import java.util.List;
import java.util.ArrayList;
import java.util.Random;

import edu.wpi.cs509.network.Connection;

/**
 * Primary class representing each game.
 * @author Scott Sussman
 **/

public class Game  {
	Gameboard gameboard = new Gameboard("gameboard");
	GameID gameID = new GameID("game ID");
	private Connection redPlayer = null;
	private Connection bluePlayer = null;
	private List<Connection> observerList = new ArrayList<Connection>();
	
	/*
	 * At init time BOTH means both players can put pieces on the board. After they submit the
	 * piece placement server logic should toggle this value between red/blue depending on turn.
	 */

	private TeamColor whoNext = TeamColor.BOTH;
	private boolean redFinishedPlacement = false;
	private boolean blueFinishedPlacement = false;
	
	// controls whether opponent pieces will be hidden
	private boolean obfuscate = true;
	
	/**
	 * Checks if the game has moved out of the "piece placement" and into the normal move phase
	 * @return True if game is in the normal move phase, False if in piece placement
	 */
	public boolean isGameStarted() {
		return whoNext != TeamColor.BOTH;
	}
	
	/**
	 * Checks if the player is currently placing pieces, or if he has signaled that he is done
	 * @param player - the connection for the player who is done placing
	 * @return True if player color has finished placing pieces.  False if player is still placing pieces
	 * or if color is BOTH
	 */
	public boolean isPlacing(Connection player) {
		
		if (player == redPlayer) {
			return !redFinishedPlacement;
		} else if (player == bluePlayer) {
			return !blueFinishedPlacement;
		} else {
			return false;
		}
	}
	
	/**
	 * Sets the internal state to reflect that this player has finished placing pieces and
	 * is ready to play
	 * @param player - the connection for the player who is done placing
	 */
	public void setDonePlacing(Connection player) {
		
		if (player == null) return;
		
		if (player == redPlayer) {
			redFinishedPlacement = true;
		} else if (player == bluePlayer) {
			blueFinishedPlacement = true;
		}
	
		/* If both players have just become ready, set this.whoNext to RED */
		if ((whoNext == TeamColor.BOTH) && 
				blueFinishedPlacement && redFinishedPlacement) {
			whoNext = TeamColor.RED;
		}
	}
			
    /** 
     * Constructor 
     * @param name - gives this game a unique name
     */
	public Game() {
		System.out.println("New Game Created:");
		System.out.println("game id: " + gameID.toString());
	}
	
	/**
	 * @return gameboard - provides caller with gameboard object
	 */
	public Gameboard getGameboard() {
		return gameboard;
	}
	
	/**
	 * When true, indicates that opponent piece values should be hidden
	 * @return
	 */
	public boolean obfuscatePieces() {
		return obfuscate;
	}

	/**
	 * @return toString - return a string with the object's contents
	 */
	public String toString() {
		StringBuilder result = new StringBuilder();
		String NEW_LINE = System.getProperty("line.separator");
		
		result.append(this.getClass().getName() + " Object {" + NEW_LINE);
		result.append(" gameboard:    " + gameboard.toString() + NEW_LINE);
		result.append("}");
		
		return result.toString();
	}
	/**
	 * Private method for adding an observer to this objects game
	 * @param user
	 * @return whether add was successful or not
	 */
	public boolean addObserver(Connection user) {
		// TODO: Add logging
		return observerList.add(user);
	}
	
	public List<Connection> getObservers() {
		return observerList;
	}
	
	/**
	 * Private method for adding a user to this game. 
	 * @param user Connection that is joining
	 * @return boolean if the player was successfully added.  Add can fail if the game is full.
	 */
	public TeamColor addPlayer(Connection user) {		
		if(bluePlayer == null && redPlayer != null) {
			bluePlayer = user;
			return TeamColor.BLUE;
		} else if (redPlayer == null && bluePlayer != null) {
			redPlayer = user;
			return TeamColor.RED;
		}
		
		Random random = new Random();
		
		if (redPlayer == null && bluePlayer == null) {
			if(random.nextBoolean()) {
				redPlayer = user;
				return TeamColor.RED;
			} else {
				bluePlayer = user;
				return TeamColor.BLUE;
			}
		}
		
		return null;
	}
	
	/**
	 * Removes an observer from the game.
	 * @param user The user's connection object
	 * @return true if successful, false otherwise
	 */
	public boolean removeObserver(Connection user) {
		return observerList.remove(user);
	}
	
	/**
	 * Removes a player from the game.
	 * @param user THe user's connection object
	 * @return true if successful, false otherwise
	 */
	public boolean removePlayer(Connection user) {
		// Put in additional check because this will get called for both players
		// And if one has left the game already (the red player) a null pointer will result.
		if (redPlayer != null && redPlayer.equals(user)) {
			redPlayer = null;
			return true;
		} else if (bluePlayer != null && bluePlayer.equals(user)) {
			bluePlayer = null;
			return true;
		}
		return false;
	}
	
	/**
	 * Public method to retrieve current game ID
	 * @return gameID
	 */
	public GameID getGameID() {
		return gameID;
	}
		
	/**
	 * getPlayer - Given the player number return the User object representing that player
	 * @param playerNumber
	 * @return Connection of the player's color.  Returns null if numbered player is not set
	 */
	public Connection getPlayer(TeamColor color) {		
		if (color == TeamColor.RED){
			return redPlayer;
		} else if (color == TeamColor.BLUE) {
			return bluePlayer;
		} else {
			return null;
		}
	}
	
	/**
	 * Gets the Color of a given connection.
	 * @param conn
	 * @return TeamColor of the given connection. If the connection is neither the Red Player nor 
	 * the Blue Player, TeamColor.BOTH is returned
	 */
	public TeamColor getColor(Connection conn) {
		if (conn == redPlayer) {
			return TeamColor.RED;
		} else if (conn == bluePlayer) {
			return TeamColor.BLUE;
		}
		
		return TeamColor.BOTH;
	}
	
	/**
	 * isJoinable - Check if the game is joinable. A game is not joinable if both player1 and player2 are not null
	 * @return joinable
	 */
	public boolean isJoinable() {
		boolean joinable = false;
		
		if (redPlayer==null || bluePlayer==null) {
			joinable = true;
		}	
		
		return joinable;
	}
	
	/**
	 * Indicates if the game contains any players or observers. Should be used
	 * before attempting to delete any game.
	 * @return true if there are no players or observers, false otherwise.
	 */
	public boolean isEmpty() {
		if ((redPlayer == null) && (bluePlayer == null) &&
				(observerList.size() == 0)) {
			return true;
		}
		return false;
	}
	
	/**
	 * isObservable() - Check if the game is observable.
	 * @return observable
	 */
	public boolean isObservable() {
		boolean observable=true;
		return observable;
	}

	/**
	 * Indicates whose turn it is by color
	 * @return
	 */
	public TeamColor getWhoseTurn() {
		return whoNext;
	}
	
	/**
	 * Change turns implements the action of switching whoNext from one
	 * color to the other.  It will pay attention to the state of each player
	 * for finished placement, which means it is safe to call this function
	 * during initial placement (no change takes place).
	 * 
	 */
	public void changeTurn() {
		if (redFinishedPlacement && blueFinishedPlacement) {
			if (whoNext != TeamColor.BOTH){
				whoNext = TeamColor.getOppositeColor(whoNext);
			}
		}
	}
}
